﻿using EventMgrLib;
using Stateless;
using Sunny.UI;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http.Headers;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace 自动化设备状态机
{
    public class StationBase
    {
        private readonly EventMgr _em;

        public StationBase()
        {
            _em = EventMgr.Ins;
            IniStateMachine();
        }
        #region 状态机

        //状态机使用步骤
        //1, 定义状态枚举
        //2，定义触发器枚举
        //3，定义状态机 StateMachine<State, Trigger> _machine
        //4, 配置状态机,链式编程，状态机.Configure(状态).允许(触发器，新的状态)
        //_machine.Configure(State.Ready)
        //.Permit(Trigger.Run, State.Running);
        //状态定义，进行时

        //Step1，定义状态
        public enum State
        {
            等待初始化,
            等待Reset,
            等待回零,
            回零中,
            Ready,
            Running,
            Pausing,
            Alarming,
            OverTime,
            等待清料,
            软件即将关闭,
            Fetal,
            InJog,
            标定中,
            老化中
        }

        //Step2 触发器，事件，信号，条件,尽量用动词
        public enum Trigger
        {
            Initlize,//初始化板卡，初始化硬件
            Reset,//清除错误，复位，上电
            Run,//自动运行
            Pause,//界面上点暂停，有时候可能客户需要碰光栅仅仅暂停
            Resume,
            Stop,//停止按键，软件界面按键
            SafeIo,//光栅，安全门，
            OverTime,
            Alarm,
            ClearAlarm,
            EmgStop,
            Close,
            Home,
            HomeFailed,
            HomeOK,
            TakeAwayProduct,
            RunOk,
            StartJog,
            StartCalibrate,
            StartBurnin
            //点检,
            //GRR
        }

        //Step3,定义状态机
        private StateMachine<State, Trigger> _machine;
        public StateMachine<State, Trigger>.TriggerWithParameters<dynamic> RunTrigger;//带参数的触发
        private State _state = State.等待初始化;
        private readonly ReaderWriterLockSlim _rwl = new ReaderWriterLockSlim();
        private readonly Stopwatch _sw = new Stopwatch();   

        //Step4,配置状态机
        private void IniStateMachine()
        {
            _machine = new StateMachine<State, Trigger>(GetState, SetState);

            RunTrigger = _machine.SetTriggerParameters<dynamic>(Trigger.Run);  //注意是 = _machine.SetTriggerParameters，而不是new

            _machine.Configure(State.等待初始化)
                .Permit(Trigger.Initlize, State.等待Reset)
                .Permit(Trigger.Close, State.软件即将关闭);

            _machine.Configure(State.等待Reset)
                .Permit(Trigger.Reset, State.等待回零)
                .Permit(Trigger.Close, State.软件即将关闭);

            _machine.Configure(State.等待回零)
                .Permit(Trigger.Home, State.回零中)
                .Permit(Trigger.Close, State.软件即将关闭);

            _machine.Configure(State.回零中)
                .Permit(Trigger.HomeFailed, State.等待Reset)
                .Permit(Trigger.HomeOK, State.Ready);

            _machine.Configure(State.Ready)
                .Permit(Trigger.Run, State.Running)
                .Permit(Trigger.StartJog, State.InJog)
                .Permit(Trigger.StartCalibrate, State.标定中)
                .Permit(Trigger.StartBurnin, State.老化中)
                .Permit(Trigger.Close, State.软件即将关闭);

            _machine.Configure(State.Running)
                .OnEntry(t => StartTimer())
                .OnExit(t => StopCallTimer())
                .OnEntryFrom(RunTrigger, Move)
                .Permit(Trigger.Pause, State.Pausing)
                .Permit(Trigger.Stop, State.等待清料)
                .Permit(Trigger.RunOk, State.Ready)
                .Permit(Trigger.SafeIo, State.Pausing)
                .Permit(Trigger.OverTime, State.Ready)
                .Permit(Trigger.EmgStop, State.Fetal)
                .Permit(Trigger.Alarm, State.Alarming);

            _machine.Configure(State.Pausing)
                .SubstateOf(State.Running)
                .OnEntry(t=> StopStation())
                .Permit(Trigger.Resume, State.Running);

            _machine.Configure(State.InJog)
                .SubstateOf(State.Running)
                .OnEntry(Joging)    
                .Permit(Trigger.RunOk, State.Ready);

            _machine.Configure(State.标定中)
                .SubstateOf(State.Running)
                .OnEntry(EntryCalibration)
                .Permit(Trigger.Resume, State.Running);

            _machine.Configure(State.老化中)
                .SubstateOf(State.Running)
                .OnEntry(EntryBurnin)
                .Permit(Trigger.Resume, State.Running);

            _machine.Configure(State.Fetal)
                .Permit(Trigger.Initlize, State.等待Reset)
                .Permit(Trigger.Close, State.软件即将关闭);

            _machine.Configure(State.Alarming)
                .Permit(Trigger.ClearAlarm, State.等待清料);

            _machine.Configure(State.等待清料)
                .Permit(Trigger.TakeAwayProduct, State.Ready);

            _machine.OnTransitioned(StateChanged);
            _machine.OnUnhandledTrigger(UnhandledTriger);
        }

        private void StopStation()
        {
            //停止工站
            _em.GetEvent<StationMessage>().Publish($"按下暂停，工站停止运动");
        }

        private State GetState()
        {
            try
            {
                _rwl.EnterReadLock();

                return _state;
            }
            catch (Exception)
            {
                return State.等待初始化;
            }
            finally
            {
                _rwl.ExitReadLock();
            }
        }

        private void SetState(State newState)
        {
            try
            {
                _rwl.EnterUpgradeableReadLock();

                try
                {
                    _rwl.EnterWriteLock();
                    _state = newState;
                }
                catch
                {
                    // ignored
                }
                finally
                {
                    _rwl.ExitWriteLock();
                }
            }
            catch
            {
                // ignored
            }
            finally
            {
                _rwl.ExitUpgradeableReadLock();
            }
        }

        private void StateChanged(StateMachine<State, Trigger>.Transition t)
        {
            string permitsTrig = _machine.PermittedTriggers.Aggregate("", (current, permit) => current + permit + ",").TrimEnd(',');
            _em.GetEvent<StationMessage>().Publish($"状态转换: {t.Source} -> {t.Destination} 触发信号 {t.Trigger}({string.Join(", ", t.Parameters)}),支持的操作：{permitsTrig}");
        }

        private void UnhandledTriger(State arg1, Trigger arg2)
        {
            _em.GetEvent<StationMessage>().Publish($"{arg1}状态不支持{arg2}触发");
        }

        #endregion 状态机

        #region 状态机外部操作接口

        internal void Trig(StateMachine<State, Trigger>.TriggerWithParameters<dynamic> trigger, dynamic parm)
        {
            if (!_machine.CanFire(trigger.Trigger))
            {
                var permits = _machine.PermittedTriggers;
                string permitsTrig = permits.Aggregate("", (current, permit) => current + permit + ",").TrimEnd(',');
                _em.GetEvent<StationMessage>().Publish($"当前状态{_machine.State}不支持 {trigger},支持{permitsTrig}");
            }
            else
            {
                _machine.Fire(trigger, parm);//内部触发状态机
            }
        }

        public bool Trig(Trigger trigger)
        {
            if (_machine.CanFire(trigger))
            {
                _machine.Fire(trigger);
                return true;
            }
            else
            {
                var permits = _machine.PermittedTriggers;
                string permitsTrig = permits.Aggregate("", (current, permit) => current + permit + ",").TrimEnd(',');
                _em.GetEvent<StationMessage>().Publish($"当前状态{_machine.State}不支持 {trigger},支持{permitsTrig}");
            }

            return false;
        }

        #endregion 状态机外部操作接口

        #region 业务逻辑

        private void StartTimer()
        {
            _sw.Restart();
            _em.GetEvent<StationMessage>().Publish($"开始计时 {DateTime.Now}");
        }

        private void StopCallTimer()
        {
            _em.GetEvent<StationMessage>().Publish($"停止计时 {DateTime.Now}");
        }

        private void Move(dynamic moveParm)
        {
            Task.Factory.StartNew(() =>
            {
                if (moveParm is MoveParam parm)
                {
                    AxisMove(parm);
                }
                else if (moveParm is CylinderParam cylinderParam)
                {
                    CylinderMove(moveParm);
                }
                //else if (moveParm is double position)
                //{   //给与默认参数
                //    var parm1 = new MoveParam(new List<int>() { 0 },
                //                new List<double>() { position },
                //                new Speed() { Velocity = 50, Acc = 500, Dec = 500 });
                //    AxisMove(parm1);
                //}
                else
                {
                    _em.GetEvent<StationMessage>().Publish("参数无法识别");
                }
            });
        }

        private void AxisMove(MoveParam parm)
        {
            //调用运动控制板卡执行运动
            var axisList = parm.AxisList;
            var posList = parm.PositionList;
            var speed = parm.Speed;
            var isRelMove = parm.IsRelMove;
            var interpolation = parm.Interpolation;

            OverTimeRetry:
            //按照上面的参数执行
            //1，绝对运动的暂停继续，可以再执行一次运动
            //2,如果是相对运动，想要暂停继续功能，可以把相对运动的位移量加上当前坐标转换成绝对运动
            RunMoveParam();
            retry:

            Stopwatch sw = Stopwatch.StartNew();
            int n = 0;
            while (sw.ElapsedMilliseconds < parm.OverTime)
            {
                Thread.Sleep(10);
                switch (_state)
                {
                    case State.Pausing:
                        goto retry;
                    case State.Ready:
                        _em.GetEvent<StationMessage>().Publish("运动完成");
                        return;
                    case State.Alarming:
                    case State.OverTime:
                    case State.等待清料:
                    case State.Fetal:
                        _em.GetEvent<StationMessage>().Publish("运动失败");
                        return;
                }

                if (++n % 100 == 0)
                {
                    _em.GetEvent<StationMessage>().Publish("自动运行中，等待到位");
                }
            }

            if (UIMessageBox.ShowAsk("超时,是否再等等"))
            {
                goto OverTimeRetry;
            }

            Trig(Trigger.Alarm);
        }

        private void CylinderMove(CylinderParam parm)
        {
            //调用运动控制板卡执行运动  
            var doList = parm.DoList;
            var doValue = parm.DoValue;
            var diList = parm.DiList;
            var diValue = parm.DiValue; 
            OverTimeRetry:
            //按照上面的参数执行
            _em.GetEvent<StationMessage>().Publish("气缸开始动作");

            retry:

            Stopwatch sw = Stopwatch.StartNew();
            int n = 0;
            while (sw.ElapsedMilliseconds < parm.OverTime)
            {
                Thread.Sleep(10);
                switch (_state)
                {
                    case State.Pausing:
                        goto retry;
                    case State.Ready:
                        _em.GetEvent<StationMessage>().Publish("运动完成");
                        return;
                    case State.Alarming:
                    case State.OverTime:
                    case State.等待清料:
                    case State.Fetal:
                        _em.GetEvent<StationMessage>().Publish("运动失败");
                        return;
                }

                if (++n % 100 == 0)
                {
                    _em.GetEvent<StationMessage>().Publish("自动运行中，等待到位");
                }
            }
            //检查气缸到位

            if (UIMessageBox.ShowAsk("超时,是否再等等"))
            {
                goto OverTimeRetry;
            }

            Trig(Trigger.Alarm);
        }


        private void RunMoveParam()
        {
            //执行运动
            _em.GetEvent<StationMessage>().Publish("执行绝对运动");
        }


        private void EntryBurnin()
        {
            Task.Factory.StartNew(() =>
            {
                while (true)
                {
                retry:
                    Thread.Sleep(1000);

                    switch (_state)
                    {
                        case State.Pausing:
                            goto retry;
                        case State.Ready:
                        case State.等待清料:
                        case State.Alarming:
                        case State.Fetal:
                            _em.GetEvent<StationMessage>().Publish($"老化停止");
                            return;
                    }
                    _em.GetEvent<StationMessage>().Publish($"老化中");
                }
            });
        }

        private void EntryCalibration()
        {
            Task.Factory.StartNew(() =>
            {
                for (int i = 0; i < 5; i++)
                {
                retry:
                    Thread.Sleep(1000);

                    switch (_state)
                    {
                        case State.Pausing:
                            goto retry;
                        case State.Ready:
                        case State.等待清料:
                        case State.Alarming:
                        case State.Fetal:
                            _em.GetEvent<StationMessage>().Publish($"标定失败");
                            return;
                    }
                    _em.GetEvent<StationMessage>().Publish($"标定中,还剩下{4 - i}次");
                }
                _em.GetEvent<StationMessage>().Publish($"标定成功");
                Trig(Trigger.RunOk);
            });
        }

        private void Joging()
        {
            Task.Factory.StartNew(() =>
            {
                
                while (true)
                {
                    retry:
                    Thread.Sleep(1000);

                    switch (_state)
                    {
                        case State.Pausing:
                            
                            goto retry;
                        case State.Ready:
                        case State.等待清料:
                        case State.Alarming:
                        case State.Fetal:
                            _em.GetEvent<StationMessage>().Publish($"点动停止");
                            return;
                    }
                    _em.GetEvent<StationMessage>().Publish($"点动中");
                }
            });
        }



        #endregion 业务逻辑

        #region 测试参数错误

        public async Task<bool> Run(double position, int overTime)
        {
            if (!_machine.CanFire(Trigger.Run))
            {
                UIMessageDialog.ShowErrorDialog(null, $"系统处于{GetState()}状态,不能启动!");
                return false;
            }

            await _machine.FireAsync(RunTrigger, position);
            _em.GetEvent<StationMessage>().Publish("当前状态:" + GetState());

            return await Task.Factory.StartNew(() => true);
        }

        #endregion
    }
}
